﻿using Logistics.Web.Models.Order;
using Nop.Core;
using Nop.Core.Domain.Directory;
using Nop.Core.Domain.Logistics;
using Nop.Services.Catalog;
using Nop.Services.Common;
using Nop.Services.Directory;
using Nop.Services.Localization;
using Nop.Services.Logistics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;

namespace Logistics.Web.Controllers
{
    public class RateController : BaseController
    {
        #region Fields
        private readonly MeasureSettings _measureSettings;
        private readonly IMeasureService _measureService;
        private readonly LogisticsInformationSettings _logisticsInformationSettings;
        private readonly IOrderProcessingService _orderProcessingService;
        private readonly ILogisticsService _logisticsService;
        private readonly IWorkContext _workContext;
        private readonly ILocalizationService _localizationService;
        private readonly IPriceFormatter _priceFormatter;
        #endregion

        #region Ctor
        public RateController(
            IWorkContext workContext,
            MeasureSettings measureSettings,
            IMeasureService measureService,
            IOrderProcessingService orderProcessingService,
            LogisticsInformationSettings logisticsInformationSettings,
            ILogisticsService logisticsSerivce,
            ILocalizationService localizationService,
            IPriceFormatter priceFormatter)
        {
            this._workContext = workContext;
            this._orderProcessingService = orderProcessingService;
            this._measureSettings = measureSettings;
            this._measureService = measureService;
            this._logisticsInformationSettings = logisticsInformationSettings;
            this._logisticsService = logisticsSerivce;
            this._localizationService = localizationService;
            this._priceFormatter = priceFormatter;
        }
        #endregion

        #region Methods
        public ActionResult Index()
        {
            return RedirectToRoute("ShippingRegulation");
        }

        [NonAction]
        protected string ValidateZipPostalCode(string fieldName, string zipPostalCode, out District district)
        {
            if (String.IsNullOrEmpty(fieldName))
            {
                throw new ArgumentNullException("fieldName");
            }

            district = null;
            zipPostalCode = zipPostalCode.Replace(" ", string.Empty).Trim().ToUpper();
            var regex = new Regex(@"^ *[ABCEGHJKLMNPRSTVXYabceghjklmnprstvxy]{1}\d{1}[A-Za-z]{1} *\d{1}[A-Za-z]{1}\d{1} *$");
            if (String.IsNullOrEmpty(zipPostalCode) || !regex.IsMatch(zipPostalCode))
            {
                return fieldName + ": " + _localizationService.GetResource("Address.Fields.Zippostalcode.Invalid");
            }

            district = _logisticsService.GetDistrictByZipPostalCode(zipPostalCode);
            if (district == null)
            {
                return fieldName + ": " + _localizationService.GetResource("Address.Fields.Zippostalcode.NoService");
            }
            return null;
        }

        public ActionResult CalculateShippingRate()
        {
            var model = new OrderPackageModel();
            model.DimensionMeasureId = _measureSettings.BaseDimensionId;
            foreach (var d in _measureService.GetAllMeasureDimensions())
            {
                model.AvaliableDimensionMeasure.Add(new AvailableMeasure()
                {
                    Text = d.Name,
                    Value = d.Id.ToString(),
                    Selected = model.DimensionMeasureId == d.Id,
                });
            }
            model.WeightMeasureId = _measureSettings.BaseWeightId;
            foreach (var w in _measureService.GetAllMeasureWeights())
            {
                model.AvaliableWeightMeasure.Add(new AvailableMeasure()
                {
                    Text = w.Name,
                    Value = w.Id.ToString(),
                    Selected = model.WeightMeasureId == w.Id,
                });
            }

            //model.IsLettersize = false;
            model.PackagePreset = null;
            return PartialView(model);
        }

        [HttpPost]
        public ActionResult AjaxCalculatePackageCost(OrderPackageModel model, string originZipPostalCode, string deliveryZipPostalCode, bool isResidential)
        {
            List<string> errors = new List<string>();
            try
            {
                if (!ModelState.IsValid)
                {
                    errors.AddRange(ModelState.Values.SelectMany(x => x.Errors.Select(e => e.ErrorMessage)));
                }
                else
                {
                    if (model.Length.HasValue && model.Width.HasValue && model.Height.HasValue && model.Weight.HasValue &&
                        model.Length.Value > 0 && model.Width.Value > 0 && model.Height.Value > 0 && model.Weight.Value > 0)
                    {
                        var dim = _measureService.GetMeasureDimensionById(model.DimensionMeasureId);
                        if (dim != null)
                        {
                            var max = MathEx.Round(_measureService.ConvertFromPrimaryMeasureDimension(
                                _logisticsInformationSettings.MaxDimensionSize, dim), 0);
                            decimal total = model.Length.Value + model.Width.Value + model.Height.Value;
                            if (total > max)
                            {
                                errors.Add(String.Format(_localizationService.GetResource("Logistics.Order.Package.Maximumsize"), (int)max, dim.Name));
                            }
                        }
                    }

                    District orginDistrict, deliveryDistrict;
                    var error = ValidateZipPostalCode(_localizationService.GetResource("Address.Fields.PickupZippostalcode"), originZipPostalCode, out orginDistrict);
                    if (!String.IsNullOrEmpty(error))
                        errors.Add(error);
                    error = ValidateZipPostalCode(_localizationService.GetResource("Address.Fields.DeliveryZippostalcode"), deliveryZipPostalCode, out deliveryDistrict);
                    if (!String.IsNullOrEmpty(error))
                        errors.Add(error);

                    if (orginDistrict != null && deliveryDistrict != null)
                    {
                        if (orginDistrict.RegionId != deliveryDistrict.RegionId)
                        {
                            errors.Add(_localizationService.GetResource("Address.Fields.Zippostalcode.NotInSameRegion"));
                        }
                    }

                    if (errors.Count == 0)
                    {
                        var pi = new PackageInformation()
                        {
                            Length = model.Length,
                            Height = model.Height,
                            Width = model.Width,
                            Weight = model.Weight,
                            //IsLettersize = model.IsLettersize,
                            DimensionMeasureId = model.DimensionMeasureId,
                            WeightMeasureId = model.WeightMeasureId,
                            InsuranceValue = model.InsuranceValue,
                            AddtionalAttributes = AddtionalAttributes.None
                        };

                        if (orginDistrict.Id != deliveryDistrict.Id)
                            pi.AddtionalAttributes |= AddtionalAttributes.InterDistrict;

                        if (isResidential)
                            pi.AddtionalAttributes |= AddtionalAttributes.Residential;

                        if (model.IsDangerous)
                            pi.AddtionalAttributes |= AddtionalAttributes.DangerousGood;

                        if (model.IsFragile)
                            pi.AddtionalAttributes |= AddtionalAttributes.Fragile;

                        if (model.IsVertical)
                            pi.AddtionalAttributes |= AddtionalAttributes.VerticalHandle;

                        if (model.InsuranceValue > 0)
                            pi.AddtionalAttributes |= AddtionalAttributes.Insurance;

                        var charge = _orderProcessingService.CaculateTotal(_workContext.CurrentCustomer, true, false, pi);
                        if (charge.Warnings.Count == 0)
                        {
                            return Json(new
                            {
                                Cost = _priceFormatter.FormatPrice(charge.SubTotalExclTax)
                            });
                        }
                        else
                        {
                            errors.AddRange(charge.Warnings);
                        }
                    }
                }
            }
            catch (Exception e)
            {
                errors.Add(e.Message);
            }

            return Json(new
            {
                Errors = errors
            });
        }
        #endregion
    }
}
